import sqlite3
import re
from nltk.corpus import wordnet as wn
from operator import itemgetter

def expand_flags (filter_flag): 
	if wn.morphy(filter_flag):
		syns = wn.synsets(wn.morphy(filter_flag)) # wn.morphy('damned') => 'damn'
	else:
		syns = wn.synsets(filter_flag) # morphy returns nil if it can't find anything
		syns = frozenset(syns)

	hyps = []
	syns = list(syns)
	for syn in syns: hyps.append(syn.hyponyms())
	hyps = sum(hyps, syns)
	flags = []
	for hyp in hyps[:]:
		flags.append(str(hyp).split('.')[0].split('\'')[1].replace('_',' '))
	for flag in flags[:]:
		flags.append(flag + 's')
	return flags;

class Recipe (object):

	def __init__(self, name, ingredients, categories, directions):
		self.ingredients = ingredients
		self.directions = directions
		self.categories = categories
		self.name = name

	def pretty_print (self):
		ret = ""
		ret += '-' * 25 + '\n' + '\n'
		ret += self.name + '\n'
		ret += '-' * 25 + '\n' +'\n'
		regex = ("  +([0-9][0-9/ ]*[^0-9]*)")
		
		for ingr in re.split(regex, self.ingredients)[:]:
			ret += ingr +'\n'
		ret += '*' * 25 + '\n' +'\n'
		for step in re.split("  +", self.directions)[:]:
			ret += step +'\n'
		return ret

	def pretty_print2 (self, arg):
		ret = "\n"
		ret += self.name +'\n'
		group = None
		if arg == 'ingredients':
			group = self.ingredients
		elif arg == 'directions':
			group = self.directions
		for val in group:
			ret += val +'\n'
		return ret

class CookBook(object):

	def __init__(self):
		self.db = sqlite3.connect('recipe_db.sqlite')
		self.db.text_factory = str		# odd workaround for 8 bit strings
		self.curs = self.db.cursor()
		self.curs.arraysize = 666

		self.name = []
		self.category = []
		self.ingredients = []

	def to_queries(self, attribute_name):
		attribute = getattr(self, attribute_name)
		
		attr_values = list(set(attribute))
		retval = ""
				
		def to_sql_like(x, y):
			return "%(_x)s LIKE '%%%(_y)s%%' OR " % {"_x":x, "_y":y}
		
		for curr in attr_values[:]:
			retval += "("																						# to use synonyms at cost of massive performance hit:
			# syns = expand_flags(curr)															# comment in this line
			# for s in syns: retval += to_sql_like(attribute_name, s)		# comment in this line
			retval += to_sql_like(attribute_name, curr)									# comment out this line
			retval = re.sub("\ OR\ $", "",retval) + ") AND "
		return retval


	@property
	def possibilities(self):
		return map(lambda f: Recipe(*f), self.recipes)

	@property
	def recipes(self):
		query = "SELECT * FROM recipes WHERE "

		for cat in ["category", "name", "ingredients"]:
			query += self.to_queries(cat)
			query = "".join(query.split("AND () "))

		query = re.sub("AND\ $", "",query)

		query += " ORDER BY directions DESC"

		self.curs.execute(query)
		return self.curs.fetchall()


	def filter (self, filter_type, filter_flag):
		if filter_type == "title": filter_type = "name"
		getattr(self, filter_type).append(filter_flag)


	def simplest(self):
		poss = self.possibilities
		if len(poss) > 0: return poss[0]
		return ()


	def printPossibilities(self):
		ret = ""
		count = 1
		for recipe in self.possibilities:
			ret += str(count)
			count += 1
			ret += ' ' + recipe.name +'\n'
		return ret


	def recipe (self, arg):

		retval = ()
		recipes = self.recipes

		try: 
			if isinstance(arg, str):
				print "I have killed hundreds of adventurers."
				retval = filter(lambda f: re.search(arg, f), recipes)[0]
			elif isinstance(arg, int):
				print "Your spine shall be my feast."
				retval = recipes [arg - 1]
			return Recipe(*retval)
		except:
			print """I\'m sorry, honey, I don\'t know a recipe like %(that)s, \
you will have to eat your own head instead."""  % {"that":str(arg)}
			return None

